模块加载方案比较
# 1.CommonJS
# 1.1 特点
- 一个文件就是一个模块,具备独立的作用域,不会污染全局
- 模块同步加载、执行,第一次加载的时候全部执行,然后缓存下来,后面执行直接使用缓存
__dirname代表当前模块文件所在的文件夹路径filename代表当前模块文件所在的文件夹路径+文件名
# 1.2 模块使用方式
require加载模块module.exports导出模块
# 1.3 举例
function add(x, y) {
console.log(x + y);
}
module.exports = {
add
};
const common = require('./common');
common.add(1, 2); // 3
2
3
4
5
6
7
8
9
10
11
# 2.ES6 加载模块
# 2.1 特点
- 默认严格模式(this 指向 undefined)
- 变量只在本模块作用域内有效
- 同一个模块加载多次,只执行一次
# 2.2 模块使用方式
import加载模块export default默认导出,如果没有 default,加载的时候需要带上{}
# 2.3 举例
// add.js
export default function add(x, y) {
console.log(x + y);
}
// 在网页中引入模块
<script type="module">
import add from "./add.js";
add(1, 2);
</script>
// 在模块中引入
import add from "./add.js";
add(2, 3); // 5
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.AMD
# 3.1 特点
- 异步并行加载,不阻塞 DOM 渲染
- 提前执行(预执行),在模块使用之前就执行完毕
# 3.2 模块使用方式
define(callback):该函数接收一个回调函数,用来定义模块require([module], callback):该函数接收两个参数,一个是要加载的模块名称数组,一个是回调函数,用来加载模块requirejs.config(obj):对 Require.js 进行配置
# 3.3 举例
需要引入 require.js
<script src="js/require.js" data-main="js/main"></script>
data-main 用于指定程序的主模块,这个文件会被 require.js 第一个加载
// 定义模块index.js
define(['index'], function(){
function person(){
console.log('jack');
}
return {
person: person
};
});
// 加载模块
require(['index'], function (module){
module.person();
});
2
3
4
5
6
7
8
9
10
11
12
13
# 4.CMD
# 4.1 特点
- 异步并行加载,不阻塞 DOM 加载
- 按需执行(需要的时候才执行)
# 4.2 模块使用方式
- require:加载模块
- seajs.config(obj):用来对 Sea.js 进行配置
- seajs.use(callback):用来在页面中加载一个或多个模块
- define(callback):用来定义模块
- require.async:用来在模块内部异步加载一个或多个模块
- module.exports 与 exports 类似:都是对外提供接口
# 4.3 举例
// 定义模块 a.js
define(function(require, exports, module) {
//引入模块
var $ = require('jquery.js')
//对外提供 price 属性
exports.price= 200;
});
// 加载模块
seajs.use(['a'],function(a){
console.log(a.price)//200
}
2
3
4
5
6
7
8
9
10
11
# 5.区别
CommonJS 模块输出的是一个值的拷贝(CommonJS 的内部变化不会影响这个值),ES6 模块输出的是值的引用
common.js 模块加载完成后,它的内部变化就影响不到输出的 count 了,从 CommonJS 中加载的变量会被缓存
let count = 1; function counter(){ count++; } module.exports = { count, counter }; const common = require('./common'); common.counter(); console.log(common.count); // 11
2
3
4
5
6
7
8
9
10
11
12
13
14而 ES6 模块的运行机制与 CommonJS 的不一样,JS 引擎对脚本进行静态分析时,遇到模块加载命令 import,就会生成一个只读引用,等到脚本真正运行时,再根据这个只读引用,到被加载的模块里面取值,从 ES6 模块中加载的变量不会被缓存
let count = 1; function counter() { count++; } export {count, counter} import {count, counter} from "./es6.js"; counter(); console.log(count); // 21
2
3
4
5
6
7
8
9CommonJS 是运行时加载,ES6 模块是编译时输出接口
CommonJS 一个模块就是一个对象,加载一个模块就是加载这个模块的所有方法,然后读取需要的方法;而 ES6 模块在编译时就完成加载,引用的时候只加载需要的方法,其他方法不加载
const { stat, exists, readFile } = require('fs');1等同于
const _fs = require('fs'); stat = _fs.stat; exists = _fs.exists; readFile = _fs.readFile;1
2
3
4上述代码的实质是加载整个 fs 模块(即加载 fs 的所有方法),生成一个对象 _fs,然后再从这个对象上读取3个方法。这种加载方式称为运行时加载,只有在运行时才能得到这个对象,导致没办法在编译阶段做静态优化
import { stat, exists, readFile } from 'fs';1上述代码的实质是从 fs 模块加载3个方法,其他方法不加载。这种加载方式称为编译时加载或者静态加载,即 ES6 可以在编译时就完成模块加载,效率比 CommonJS 加载模块的方式高
CommonJS 模块的顶层 this 指向当前模块,ES6 模块的顶层 this 指向 undefined
# 6.模块间如何保持独立
在 nodejs 中,所有的 js 文件在node中执行前,都会被包装进
define(require, exports, module, _filePath, _fileName)
该函数会返回 exports 对象,通过 exports 向外暴露属性